home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The X-Philes (2nd Revision)
/
The X-Philes Number 1 (1995).iso
/
xphiles
/
hp48hor2
/
mlinput.doc
< prev
next >
Wrap
Text File
|
1995-03-31
|
41KB
|
933 lines
HP48SX Keyboard Input
A Guide for the Machine Language Programmer
by
Joe Ervin
1 INTRODUCTION
In this document, we will examine the workings of the HP48 keyboard
hardware and how to do your own keyboard input on the HP48 from
machine language. Additionally, we will describe how the HP48
keyboard input scheme works during normal operation, and how you can
disable the normal operation and take over direct control of the
keyboard.
2 OVERVIEW OF HP48 KEYBOARD INPUT
During normal operation, the CPU scans the keyboard for key presses
every 1ms. This 1ms keyboard scan is performed directly by the CPU
hardware with no involvement from software, and therefore has a
negligible effect on CPU performance. During this automatic keyboard
scan, the entire keyboard is scanned in a single operation. If no
keys are currently being pressed, then nothing else happens and the
CPU continues executing instructions normally. If, however, this
automatic keyboard scan indicates that one or more keys are being
pressed, then the hardware interrupts the CPU. The HP48's interrupt
handler then determines the exact key or keys which are being pressed
and updates the keyboard data structures in memory to reflect the new
state of the keyboard. The keyboard data structures are described
later in this document.
In addition to determining which specific keys are pressed, the HP48's
interrupt handler also scans all other possible sources of interrupts
and services them appropriately. In the case of pressed keys, the
interrupt handler additionally schedules a timer interrupt for 1/16 of
a second into the future before returning to the interrupted program.
When the timer expires, another interrupt is generated, and the whole
operation repeats. As a result, the CPU is interrupted 16 times per
second, for as long as a key is held down.
The use of the timer interrupt is necessary because the keyboard
interrupt hardware only generates interrupts when keys are pressed,
Page 2
i.e. no interrupts are generated directly as a result of keys being
released. Therefore, by continually scheduling the timer interrupt to
retrigger an interrupt in the near future, the interrupt handler can
effectively "poll" the keyboard 16 times per second, checking for keys
being released. This is done so that the "key-released" event can be
recorded in the keyboard data structures, described below. Machine
language applications can then check these data structures to
determine the exact state of each key on the keyboard.
3 SCANNING THE KEYBOARD FROM MACHINE LANGUAGE
Unlike the automatic 1ms keyboard scan performed by the CPU's
hardware, the interrupt handler must force keyboard scans from
software to determine the exact state of the keyboard. This is
necessary for the interrupt handler because any interrupts generated
by the 1ms keyboard scan indicate only that at least one key has been
pressed. The interrupt handler then must scan the keyboard row by row
to determine exactly what keys are being pressed. This same approach
can be used from machine language programs to scan the keyboard.
Scanning the keyboard from software happens in two general phases; an
output phase and an input phase. During the output phase, software
causes the HP48 to output signals to the keyboard which indicate the
exact row or rows which are being scanned. Then, during the input
phase, the software reads the keyboard to see which of the scanned
rows have keys pressed.
The keyboard is wired in a matrix as shown in Figure 1.
Page 3
IN #20 #10 #08 #04 #02 #01
OUT (bit) 5 4 3 2 1 0
#100 8 B C D E F
#080 7 PRG CST VAR up NXT
#040 6 STO EVL <<< dwn >>>
#020 5 COS TAN sqt pwr inv
#010 4 ON* ENT +/- EEX DEL <==
#008 3 alp SIN 7 8 9 /
#004 2 yel MTH 4 5 6 x
#002 1 blu A 1 2 3 -
#001 0 ' 0 . SPC +
(*) The ON key is actually in a column of its own.
The ON key is represented in bit 15 of the data returned
from an IN.4 instruction. #xxx refers to the keyboard scan
bits driven with the OUT instruction.
Figure 1
3.1 The OUT Instruction.
During the first phase of the keyboard scan, software executes the
OUT.X C instruction, which loads the HP48's output register with the
contents of C.x, and then drives those contents into the key matrix.
The bits in OUT<8:0> are connected to keyboard ROWS <8:0>
respectively. Each "1" bit in OUT<8:0> therefore drives a HIGH
voltage level out to the corresponding keyboard row. If any key in
that row is currently pressed, then the HIGH level propagates to the
bit of the input register which corresponds to the COLUMN of the key
being pressed. Normally a HIGH voltage level on the input port causes
an interrupt, but we will show later how to disable this interrupt so
that the machine language program will be unhindered by the HP48's
interrupt handler.
Page 4
3.2 The IN.4 Instruction
During the second phase of the keyboard scan, software executes the
"IN.4 C" instruction to see if any keys in the scanned rows were
pressed. In this way, a machine language program can scan the
keyboard to test whether specific keys are being pressed.
With the exception of the ON key, the keys which are "visible" to the
input port is a function of the bit pattern driven during the OUT
instruction. For example, if the output register contains 1FFh, then
all keyboard rows are visible to the IN register. If, say, the [<-]
key is pressed (or any other key or keys in that column), then after
the "IN.4 C" instruction executes, bit 0 of the C register will be
set, indicating that at least one key in the rightmost column of the
scanned rows was pressed. Note that if more than one keyboard row is
being scanned, then the data in IN<5:0> does not indicate a specific
key, but rather a specific column of any of the scanned keyboard rows,
as depicted by Figure 1. The state of the ON key is always reflected
in bit 15 of the data returned from the IN.4 instruction.
Because of the row/column wiring of the HP48 keyboard, in order to
determine exactly which keys are pressed it is necessary to perform
multiple OUT/IN pairs in which each "OUT.x C" scans only a single
keyboard row.
For example, "OUT.x C" with C.x=001h scans only the bottom row of the
keyboard; C=002h scans the second row from the bottom; C=004h scans
the third row from the bottom, and so on. In this way, any set bits
in the data returned by the IN.4 instruction indicate that a specific
key is being pressed. This is what the HP48's interrupt handler does
to determine the exact status of the keyboard after the 1ms keyboard
scan has generated an interrupt due to one or more keys being pressed.
During normal operation, the interrupt handler then updates the
"KeyBuf" and the "KeyState" in memory (described below) to indicate
the key status to the rest of the RPL system.
3.3 Interrupt Woes
In the process of servicing other I/O devices, checking battery
voltage, etc., the interrupt handler executes many hundreds of
instructions which have nothing to do with the keyboard, making the
handling of keyboard input through this mechanism very inefficient in
terms of CPU utilization. More significantly, however, the interrupt
handler debounces the keyboard by capturing the state of the entire
keyboard repeatedly, waiting 2ms between samples, until it sees
exactly the same keyboard state for 5 consecutive samples. Because of
this, the interrupt handler is guaranteed to require over 10ms to
detect a single keystroke.
Furthermore, the keyboard service routine in the interrupt handler is
implemented as a loop which synchronizes itself to the 16Hz timer
(TIMER1). If the keyboard service routine detects any keys being held
down, then after updating the keyboard data structures (see below) it
Page 5
checks to see whether there is enough time before the next 16Hz tick
to do another full pass through the keyboard service routine and if
so, the code loops back to the top of the keyboard service routine.
If the next 16Hz clock tick is less than approximately 17ms into the
future, then the keyboard service routine exits, and the interrupt
handler completes. However, since the interrupt handler scheduled
TIMER1 to interrupt on the next 16Hz tick, the CPU will bounce back
into the interrupt service routine in just a few milliseconds. Thus,
holding down a key while the HP48's normal keyboard interrupt service
is in operation causes the keyboard interrupt service routine to hog
approximately 75% of the CPU, leaving the remaining 25% for your
application.
When writing machine language programs that do not require much CPU
power, this may not be a concern and it may be desirable to allow the
interrupt system to handle keyboard input normally. In this case, the
machine language program can retrieve keyboard status from the two
keyboard data structures described below. However, for CPU-intensive
applications it is generally desirable to disable the HP48's normal
keyboard interrupts and perform keyboard I/O directly in the
application.
3.4 HP48 Keyboard Data Structures
There are two main keyboard-related data structures that the HP48
keeps in memory. The first is called the "KeyBuf", and occupies 34
nibbles starting at #704EA. The second is called the "KeyState" and
occupies 13 nibbles starting at #704DD. Additionally, there are three
other datum kept in memory which the interrupt handler uses in
conjunction with the keyboard. These are the "ORshadow" (Output
Register shadow), which occupies 3 nibbles at #704C3, "KBdisable",
which is a single nibble at #704DC, and a two nibble value at #706C3
representing the state of the display annunciator flags at 10B/10C.
These data structures are described below.
3.4.1 The KeyBuf
The first two nibbles of the KeyBuf are the "get" and "put" pointers,
respectively, and the remaining 32 nibbles comprise a 16-entry key
buffer, with each entry occupying one byte. The "get" pointer
provides an index into the KeyBuf for the next available key code.
Similarly, the "put" pointer provides a index to the next entry in the
KeyBuf to be written. If the "get" and "put" pointers are equal, then
the buffer is empty. The "get" and "put" pointers point to byte
locations within the buffer.
You may have noticed how your calculator beeps at you defiantly when
you have exceeded this 16 entry type-ahead buffer. This is the
interrupt handler telling you that there is no more room in the
KeyBuf.
Page 6
The key codes which are used to represent keys in the KeyBuf are
different than the scan patterns read in during the OUT/IN procedure.
Each key is given a unique 1-byte key code as follows: key codes are
numbered sequentially from the upper left of the keyboard counting
across and down. Thus a key code of 1 represents the [A] key, 2 for
[B], 3 for [C]..., #19h for [ENTER],... #1Fh for [7],... #31h for
[+]. Note that four keys do not obey this ordering: [alpha] is #80h,
[leftshift] is #40h, and [rightshift] is #C0h, and [ON] has no key
code. The ON key is handled as a special case by the interrupt
handler and does not appear in KeyState or in the KeyBuf.
If the keyboard service routine detects the presence of [alpha],
[leftshift], or [rightshift] at the same time as another key, then the
key code for the "shift" key is ORed into the keycode for any
non-"shift" keys which may also be pressed, and the resulting keycodes
are inserted into the KeyBuf. For example, the "right-shifted"
keycode for the [A] key is [rightshift]![A] = C0h!01h = C1h.
Similarly, the keycode for a "alpha-shifted" [ENTER] is
[alpha]![ENTER] = 80h!19h = 99h. If one of the shift keys is detected
alone, then its keycode is simply inserted into the KeyBuf.
The code example in Appendix A shows a simple way to remove keys from
the key buffer. In addition, this code example shows how to put the
calculator into "light sleep" awaiting a key press.
3.4.2 The KeyState
The 13 nibbles at location #704DD provide a bit pattern which reflects
the status of the keyboard, with each bit representing the state of a
specific key. This bit pattern is updated whenever a key is pressed,
and is also updated when keys are released via a timer interrupt as
described above. When a key is pressed, its corresponding bit is set
to 1. There are 13 nibbles, 4 bits each, making 52 bits. The [ON]
key is not represented in KeyState. There are 48 keys remaining, so
four bits are unused. The low bit is unused, the next bit corresponds
to the bottom rightmost key [+], the one following corresponds to SPC,
then comes period [.], [0], ['], after which immediately follows the
next row: [-], [3], and so forth, up to the upper leftmost key, which
is [B]. (See Figure 1 above for the physical layout of the keyboard).
Below is a table of all key bit codes. More than one bit is set if
more than one key is being held down simultaneously.
Page 7
Key 704E7 704E2 704DD Key 704E7 704E2 704DD
B | 1 | | | <== | | 1 | |
C | 8 | | | alp | | 8| |
D | 4 | | | SIN | | 4| |
E | 2 | | | 7 | | 2| |
F | 1 | | | 8 | | 1| |
PRG | 8| | | 9 | | |8 |
CST | 4| | | / | | |4 |
VAR | 2| | | yel | | |2 |
up | 1| | | MTH | | |1 |
NXT | |8 | | 4 | | | 8 |
STO | |4 | | 5 | | | 4 |
EVL | |2 | | 6 | | | 2 |
<<< | |1 | | x | | | 1 |
dwn | | 8 | | blu | | | 8 |
>>> | | 4 | | A | | | 4 |
COS | | 2 | | 1 | | | 2 |
TAN | | 1 | | 2 | | | 1 |
sqt | | 8 | | 3 | | | 8 |
pwr | | 4 | | - | | | 4 |
inv | | 2 | | ' | | | 2 |
ENT | | 1 | | 0 | | | 1 |
+/- | | 8 | | . | | | 8|
EEX | | 4 | | SPC | | | 4|
DEL | | 2 | | + | | | 2|
Figure 2 KeyState
3.4.3 KBdisable And ORshadow
As with any computer system that utilizes interrupts, the servicing of
interrupts must be transparent to the currently running application,
aside from the time delay associated with servicing the interrupt. In
order to do this, the entire state of the CPU is saved at the
beginning of the interrupt handler, and restored again at the end.
Unfortunately, the contents of the OUT register is not readable, so it
is not possible to directly save its contents. Since the interrupt
handler invariably modifies the contents of the OUT register, the HP48
interrupt system requires applications to maintain a copy of the OUT
register in RAM. The 3 nibbles stored at #704C3, designated as the
"ORshadow", is used for this purpose.
The ORshadow is used by applications to shadow the contents of the OUT
register. It is the responsibility of any application which intends
to use the OUT register for its own purposes to modify the ORshadow
along with the OUT register. By doing this, the interrupt handler is
provided with a readable copy of the contents of the OUT register so
that if an interrupt should occur while the application is using the
OUT register, the interrupt handler will restore the OUT register to
Page 8
its correct contents before returning control to the application.
The KBdisable flag is a single nibble which applications can write
with a nonzero value to indicate to the interrupt handler that the
keyboard is currently being scanned by an application. When this
nibble is nonzero, the interrupt handler will not run the keyboard
service routine. This is very useful for applications which have
their own keyboard input routines as it allows the programmer to
prevent the high CPU utilization by the interrupt handler as discussed
above. The programmer should be aware, however, that the annunciator
flags will still be updated when the corresponding keys are pressed,
unless interrupts are disabled altogether as described below.
3.4.4 Utilizing The Built-in Data Structures
Using the KeyBuf and KeyState data structures for your keyboard input
is very straightforward. The routine provided in Appendix A shows how
to access the KeyBuf, and accessing the KeyState is trivial. An
application need only examine the appropriate bits in KeyState to
determine if any given keys are being held down. For applications
which can tolerate the additional CPU load of the interrupt handler,
it probably makes sense to just allow the normal interrupt mechanism
to service the keyboard, and then for the application to utilize the
KeyState and KeyBuf data structures. It is generally only for
applications which perform CPU intensive or time-critical operations
that manual keyboard scanning techniques are required. The following
sections will discuss this issue further, as well as how to write your
own keyboard input routines.
4 CUSTOM KEYBOARD I/O
The main motivation for writing your own keyboard input routines is to
steal back valuable CPU time from the interrupt system for
applications that need it. In order to recover the CPU time that the
HP48 normally uses up in the keyboard interrupt service routine, you
will need to write your own keyboard I/O routines, or you can just
cut/paste the routines given later in this document.
4.1 Controlling The HP48 Interrupt System
Writing your own keyboard I/O routine does little good unless one also
disables the normal keyboard interrupt mechanism. Fortunately, there
are a few options open to the machine language programmer in this
regard. Described below are some general approaches to disabling
normal keyboard servicing.
Page 9
4.1.1 The Big Hammer
The first method, which we will call "The Big Hammer", is to clear bit
15 of the status register. This bit is checked at the top of the
interrupt handler, and if it is clear then the interrupt handler
disables further interrupts and returns to the interrupted program.
Thus, clearing ST<15> effectively shuts off all I/O on the calculator,
namely the keyboard. The interrupt system also sets ST<14> to
indicate that an interrupt request has been posted but was not
serviced.
One disadvantage to the "big hammer" approach are that once you clear
ST<15>, if a code bug causes your program to "hang" with ST<15>=0, you
have no control over the calculator. You can't turn it off; you can't
do [ON]-[C]; you can't do [ON]-[A][F]. The only thing you _can_ do is
to pull off the rubber foot hiding the reset button and jam a paper
clip in the hole. Not much of a way to quit your application.
Another problem is that preventing certain interrupts from being
serviced for extended periods of time can lead to problems in the
calculator. Since the interrupt handler does nothing if ST<15> is
clear, none of the possible interrupt sources in the HP48 will be
serviced. This includes, among other things, the low battery detect
circuitry. Hence if a machine language program which has cleared
ST<15> is left running, it can drain the batteries completely,
resulting in total loss of memory. Normally the low-battery detect
circuitry would interrupt the CPU, allowing the interrupt handler to
safely shut the system down into a very low power consumption state,
thus preserving RAM. With ST<15> clear, however, this safety net is
removed. For programs which run for durations of less than several
hours, however, this should not be a problem.
Other sources of interrupts such as timer rollover and serial I/O
activity will be totally ignored as long as ST<15> is clear. However,
the lack of interrupt-driven serial I/O capabilities may not be an
issue for many applications, and interrupt requests due to rollover of
the 32-bit hardware timer (TIMER2) can be ignored for 72 hours without
effecting the calculator's sense of time.
An example of when clearing ST<15> can be particularly useful is when
the keyboard is actually being scanned. In this way, software can
avoid the need to shadow the OUT register in ORshadow. This works
because no interrupts are possible while ST<15>=0. The application
should then set ST<15>=1 after completing the keyboard scan to allow
other interrupts, if desired. See the ENABLE_INTR routine in Appendix
B for more information on how to re-enable interrupts after disabling
them via ST<15>.
4.1.2 The Little Hammer
Another approach to disabling the keyboard interrupts is to shut off
keyboard scanning at the source by executing an INTOFF instruction.
Page 10
This disables the automatic 1ms keyboard keyboard scan described
above. As a result, interrupts no longer occur due to key presses,
with the exception of [ON] which always causes an interrupt.
Furthermore, since we have not disabled I/O altogether such as is the
case when ST<15> is cleared, we can still abort our ML application by
doing [ON]-[C] if the need arises.
The only minor pitfall to this technique is that the INTOFF
instruction prevents _only_ the keyboard interrupts. If one of the
other devices in the system causes an interrupt, then the interrupt
handler will still execute. As it turns out, the vast majority of the
time spent in the interrupt handler is due to the keyboard service
routine. Therefore, aside from the keyboard service routine in the
interrupt handler, allowing interrupts does not cost very much in
terms of CPU time.
Fortunately, the keyboard service routine can be completely disabled
by writing the KBdisable nibble to a nonzero value, so that if an
interrupt should occur, the keyboard service routine will not be
executed. The programmer should note that the [alpha], [leftshift],
and [rightshift] keys are polled outside the keyboard service routine
and the corresponding display annunciators updated. Unfortunately,
there is no way to prevent this from happening aside from turning off
interrupts altogether via ST<15>. This is generally not necessary,
however, since in applications which do not use serial I/O, interrupts
should not occur.
The programmer should be aware that the interrupt service routines for
the serial I/O contain INTON instructions, so if serial I/O is used,
then INTOFF instruction will need to be repeatedly executed to prevent
keyboard interrupts from occurring.
4.1.3 Recommendations
Because of the different features built into the HP48 interrupt
service routine, there are several approaches that a ML programmer can
take to disable normal keyboard operations. While each technique has
its own strengths and weaknesses, there are some general programming
practices that can lead to "cleaner" solutions. Below are a few hints
to keep in mind with respect to programming for custom keyboard input.
1. Use INTOFF as a general technique to disabling keyboard
interrupts, with critical "uninterruptable" sections of code
protected by clearing ST<15>. This generally works well,
although there are a few things to keep in mind.
- After re-enabling interrupts by setting ST<15>=1, you
need to check ST<14> to determine whether an interrupt
was requested while interrupts were disabled. ST<14>=1
means an interrupt was requested but has not yet been
serviced. Basically what happened is that an interrupt
occurred, but since ST<15> was clear the interrupt
handler immediately terminated via a RET instruction
Page 11
rather than servicing the interrupt and terminating with
an RETI instruction, as it does normally when ST<15>=1.
The interrupt handler sets ST<14>=1 to inform the
application that an interrupt is "pending".
The implication of this is that the HP48 believes that it
is still executing in the interrupt handler (since no
RETI has been executed since the last interrupt) and will
not allow further interrupts until an RETI instruction is
executed. See the ENABLE_INTR routine in Appendix B for
an example of how to handle this.
- The Ticking Clock display or any user alarms which come
due will be serviced whenever interrupts are enabled.
This can lead to the INTON instruction being executed in
the interrupt handler. Once this occurs, then it is
possible for keystrokes to cause interrupts.
- If the Keyboard interrupt service routine has been
disabled via the KBdisable flag, then keypresses should
not make it into the KeyBuf, even if interrupts occur.
If the application does not modify the KBdisable flag,
then the KeyBuf may need to be periodically flushed to
remove any keypresses that sneak into the KeyBuf.
- Because any interrupts may lead to the execution of the
INTON instruction, the application should periodically
execute an INTOFF instruction.
2. Use "ST<15>=0" sparingly. Try to keep sections of code which
are protected from interrupts via the clearing of ST<15> as
small as possible, and try to keep the "CLRB 15, ST" and
"SETB 15, ST" instructions as local to each other as
possible. Remember, if you make a mistake and leave
ST<15>=0, all keyboard control is lost. Try to keep
"uninterruptable" sections of code as small as possible, such
as in the code example of Appendix B when the OUT register is
being modified.
4.2 Example Keyboard Input Routines
The code example in Appendix B shows how custom keyboard I/O routines
can be written. The code shown in this example is actually the
keyboard scanner process which runs in the game program "Vaders".
Vaders was written using MPE, a machine language multiprogramming
environment for the HP48. The keyboard scanner process in Appendix B
runs concurrently with the other processes which make up the Vaders
game, making the keyboard scanning transparent to the other processes
which comprise the game.
The example code maintains two data structures which reflect the
Page 12
status of the keyboard in a manner similar to the "KeyBuf" and
"KeyState" data structures maintained by the HP48's interrupt system.
APPENDIX A
KEYBUF CODE EXAMPLE
;;+
;;
;; Keyboard Interface.
;;
;; Keyboard scan codes are numbered 1 for [A], 2 for [B], 3 [C]...
;; #19h [ENTER]... #1Fh [7]... #31h [+]. [alpha] is #80h, [yellow] is
;; #40h, and [blue] is #C0h. ON has no scan code.
;;
;; This program reads the keyboard buffer. If a key is present, it
;; returns it in A.A. If no key is present, it enters light sleep and
;; waits for one.
;;
;; kb_poll polls the keyboard buffer, carry set if non-empty, key in A.A.
;; kb_get does the same, but waits until a key is pressed.
;;
;; Jan Brittenson, April 1991
;; This program is in the Public Domain
;;
;;-
radix ^d16
event_mask = 10e
;; Poll keyboard buffer
kb_poll:
move.5 keybuf+1, d0 ; KB Put ptr
move.s @d0, a ; A.S = put ctr
dec d0
move.s @d0, c ; C.S = get ctr
breq.s c, a, $100 ; Ctrs are equal - buffer empty
move c.15, p ; P = get ctr
inc.s c ; Remove key
move.s c, @d0
swap c, d0
add p+1, c
add p+1, c ; C += get ctr, in bytes
KEYBUF CODE EXAMPLE Page A-2
clr p
move c, d0 ; D0 = &next key
clr.a a
move.b @d0, a ; A.A = key
retsetc
$100:
clr.a a
retclrc
;; Wait for a key to become pressed, then return scan code in
;; A.B. Uses C.A and B.B.
kb_get:
call kb_poll ; Get key, if any
retcs ; Return if there was a key in the buffer
; No keys are down - enter light sleep
move.5 event_mask, d0
move.p1 8, c
move.1 c, @d0
rsi
shutdn ; Go asleep
move.p1 0xc, c ; Restore event mask
move.1 c, @d0
jump kb_get ; Check buffer again
APPENDIX B
CUSTOM KEYBOARD I/O ROUTINES.
;*********************************************************************
;*********************************************************************
; This process is responsible for scanning the keyboard and updating
; the KEY_SCAN and NEW_KEYS data structures. The routines GET_KEYS
; and GET_NEW_KEYS are used to check these two data structures,
; respectively, for key presses.
keybuf = ^x704EA ; keyboard buffer.
DO_IN_4 = ^x1160 ; Does IN.4 C.
KEY_SCAN: DATA.w 0 ; This data structure holds a shadow
; of the status of the keyboard. Each
; bit represents a key, although some
; bits are unused.
NEW_KEYS: DATA.w 0 ; This word indicates whether any keys
; have been newly pressed. Each bit
; represents one key, although some
; bits are unused.
GET_KEYS: ; This routine just looks in the key_scan data for
; the presense of any of the keys specified in the
; key mask in C.w.
addr key_scan, d0 ; Address of key_scan data in D0.
move.w @d0, a ; Get key status.
and.w a, c ; Allow only the selected keys.
ret
GET_NEW_KEYS: ; This routine checks the new_key data for new key
; presses. The calling procedure must supply a key
; mask in C.w. Any new keys selected by the mask
; are cleared from the new_key data. A nonzero value
; is returned in C.w representing which of the selected
; keys were pressed.
addr new_keys, d0 ; Trashes A.
CUSTOM KEYBOARD I/O ROUTINES. Page B-2
move.w @d0, a ; Get new_keys.
and.w a, c ; Allow only selected keys.
not.w c ; Invert result and apply to A
and.w c, a ; to clear selected keys from new_keys.
move.w a, @d0 ; Write updated new_keys data.
not.w c ; Invert result and apply to A
ret
ENABLE_INTR: ; This routine turns interrupt servicing back
; on after an interrupts were disabled by
; clearing ST<15>. This routine checks for
; pending interrupts and ensures that they are
; serviced.
setb ^d15, st ; Reenable IO interrupt service. We want to
; do this so that any important interrupts in
; the system will still be recognized.
brbc ^d14, st, $1 ; If ST<14> is set, then we have missed an
; interrupt while ST<15> was clear, so we
; need to re-enable interrupts.
clrb ^d14, st ; Clear the pending interrupt flag.
RSI ; Reset the keyboard interrupt state machine.
$1: RETI ; Re-enable interrupt servicing. If there is
; an pending interrupt, it will be serviced
; now.
PROCESS8_INIT:
; R0: Holds current key status.
; R1: Holds the old key status.
; R2:
; R3:
; R4:
cur_process_start process8_code
addr new_keys, d1 ; D1 holds pointer to new_keys data.
addr key_scan, d0 ; Get address of the key scan bit pattern.
clr.w a
move.w a, r0
move.w a, r1 ; Initialize current and old key status.
move.w a, @d0 ; Clear the keyboard scan pattern.
move.w a, @d1 ; Clear new_keys.
PROCESS8_CODE:
intoff ; Shut off the system 1ms keyboard scan,
again.
; This is necessary because
; if other interrupts have occurred, then the
; INTON instruction may have been executed in
; the interrupt handler, re-enabling keyboard
; interrupts.
CUSTOM KEYBOARD I/O ROUTINES. Page B-3
move.5 keybuf+1, d0 ; Point at "put" pointer.
move.1 @d0, c ; Get the "put" pointer.
dec d0 ; Point at "get" pointer.
move.1 c, @d0 ; Flush the keybuf just in case any characters
; have snuck into the keybuffer.
addr key_scan, d0 ; Get address of the key scan bit pattern.
; First we just want to check to see if any keys are being pressed.
clrb ^d15, st ; Shut off IO interrupt service for now. This
; needs to be done here so that any interrupts
; generated in the process of scanning the
; keyboard will be ignored by the system.
; Keyboard interrupts are possible because
; if other interrupts have occurred, then the
; INTON instruction may have been executed in
; the interrupt handler, re-enabling keyboard
; interrupts.
clr.w c
move.p3 ^x1FF, c ; OUT value for entire keyboard.
out.x c
call.a do_in_4
clr p
brnz.w c, $1 ; If no keys are pressed we just fall through.
clr.w c
move.w c, @d0 ; Zero out the key_scan scoreboard.
move.w r0, a ; Old "current" key status.
move.w a, r1 ; R1 holds the old key status.
move.w c, r0 ; R0 holds the current key status.
jump.4 process8_sched ; Go reschedule the process.
$1: ; Now we need to scan the keyboard and build up the key scoreboard.
addr new_keys, d1 ; D1 holds pointer to new_keys data.
clr.w a ; Temp storage for scanned keys.
clr.w c
move.p3 ^x100, c
move.w c, d ; Leave a copy in D, where it will be shifted.
$3: move.w d, c ; Put scan pattern into C.
out.x c ; scan the next row.
call.a do_in_4 ; Read the keys.
add.w a, a ; Shift the data in A 6 bits to the left.
add.w a, a
add.w a, a
add.w a, a
add.w a, a
add.w a, a
OR.w C, A ; Add in the new keys to the data in A.
srb.w d ; Shift to look at next keyboard row.
brnz.w d, $3 ; Do another row if not done yet.
move.w a, @d0 ; Write out the scanned keyboard data.
move.w r0, c ; Old "current" key status.
move.w c, r1 ; R1 holds the old key status.
move.w a, r0 ; R0 holds the current key status.
CUSTOM KEYBOARD I/O ROUTINES. Page B-4
not.w c ; Invert the old key status...
and.w a, c ; and AND in the new keys. The resulting word
; in C indicates the "new" key presses.
move.w @d1, a ; Get existing value of NEW_KEYS data.
or.w a, c ; Add in the new keys without destroying any
; "new" keys that haven't been serviced yet.
move.w c, @d1 ; D1 points to New_keys data.
clr.w c
move.p3 ^x1FF, c ; OUT value for entire keyboard.
out.x c ; Just to reset things so when we exit to
; the RPL environment the system can still
; read the keyboard. This is needed because
; the 1ms keyboard does not reload the OUT
; register.
PROCESS8_SCHED:
call.4 save_context ; Save the process context.
call.4 enable_intr ; Re-enables I/O by setting ST<15>=1 and
; executes an RETI instruction to allow
; interrupt servicing.
CLR.W c
MOVE.P3 ^xFF, c ; Reschedule this process to look at the
; keyboard roughly 30 times per second.
call.4 resch_cur ; Reschedule the current process.
PROCESS8_EXIT:
JUMP.4 TO_SCHEDULER ; Return control to the scheduler.
; Thus ends process #8.